#include "MeshInstances.h"

#define SCALE_FACTOR 0.2f

void CCameraMeshInstance::CommonInit(const Matrix4f& Rotate,const Matrix4f& translate, CCameraMesh* pMesh, float f)
{
	m_pMesh = pMesh;
	Matrix4f Scale, CamTranslation, CamRotation, Projection;
	float scale = f / 500;
	Scale.InitScaleTransform(SCALE_FACTOR * scale, SCALE_FACTOR * scale, SCALE_FACTOR * scale);
	m_World = translate*Rotate*Scale;
	m_Scale.InitIdentity();
	//texture shit
	m_Image.Load();
	m_color = Vector3f(1, 0, 0);
}

CCameraMeshInstance::CCameraMeshInstance(const Matrix4f& Rotate,const Matrix4f& translate, CCameraMesh* pMesh, char* ImageFileName, UINT Index) :
										m_Image(GL_TEXTURE_2D, ImageFileName), m_Index(Index)
{
	
	CommonInit(Rotate, translate, pMesh, 1);
}

CCameraMeshInstance::CCameraMeshInstance(const Matrix4f& Rotate,const Matrix4f& translate, CCameraMesh* pMesh, cv::Mat Image, UINT Index, float f) : m_Image(GL_TEXTURE_2D, Image), m_Index(Index)
{
	CommonInit(Rotate, translate, pMesh, f);
}

void CCameraMeshInstance::Draw() const
{
	//drawing the camera frustrum
	glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);
	CCameraMesh* pMesh = (CCameraMesh*)m_pMesh;
	VISIO_ASSERT(pMesh);
	glBindBuffer(GL_ARRAY_BUFFER, pMesh->m_VBO[0]);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pMesh->m_IBO[0]);
	
	GLuint progObject = pMesh->m_GLMethod.pProgram->GetProgramObject();
	glUseProgram(progObject);
	GLuint WVPLoc = pMesh->m_GLMethod.vsVars["WVPMat"];
	GLuint PosLoc = pMesh->m_GLMethod.vsVars["Position"];
	GLuint TexLoc = pMesh->m_GLMethod.vsVars["iTexCoord"];
	GLuint bStrechLoc = pMesh->m_GLMethod.vsVars["bStreched"];
	GLuint StrechFactor = pMesh->m_GLMethod.vsVars["strechFactor"];
	GLuint SamLoc = pMesh->m_GLMethod.psVars["gSampler"];
	GLuint bSampleLoc = pMesh->m_GLMethod.psVars["bSample"];
	GLuint ColLoc = pMesh->m_GLMethod.psVars["col"];

	glUniformMatrix4fv(WVPLoc, 1, GL_TRUE, &m_WVP.m[0][0]);
	glUniformMatrix4fv(StrechFactor, 1, GL_TRUE, &m_Scale.m[0][0]);
	glUniform1i(SamLoc, 0);
	glUniform1i(bSampleLoc, 0);
	glUniform3f(ColLoc, m_color.x, m_color.y, m_color.z);
	glEnableVertexAttribArray(PosLoc);
	glEnableVertexAttribArray(TexLoc);
	glEnableVertexAttribArray(bStrechLoc);
	glVertexAttribPointer(PosLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
	glVertexAttribPointer(TexLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)12);
	glVertexAttribPointer(bStrechLoc, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)24);

	glDrawElements(GL_TRIANGLES, pMesh->m_NumIndices, GL_UNSIGNED_INT, 0);

	//drawing the picture on the back wall
	m_Image.Bind(GL_TEXTURE0);
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);
	glUniformMatrix4fv(WVPLoc, 1, GL_TRUE, &m_WVP.m[0][0]);
	glUniformMatrix4fv(StrechFactor, 1, GL_TRUE, &m_Scale.m[0][0]);
	glUniform1i(SamLoc, 0);
	glUniform1i(bSampleLoc, 1);
	glUniform3f(ColLoc, m_color.x, m_color.y, m_color.z);
	glEnableVertexAttribArray(PosLoc);
	glEnableVertexAttribArray(TexLoc);
	glEnableVertexAttribArray(bStrechLoc);
	glVertexAttribPointer(PosLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
	glVertexAttribPointer(TexLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)12);
	glVertexAttribPointer(bStrechLoc, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)24);
	UINT offsetOfBackWall = (pMesh->m_NumIndices - NUM_INDICES_IN_QUAD) * sizeof(UINT);
	glDrawElements(GL_TRIANGLES, NUM_INDICES_IN_QUAD, GL_UNSIGNED_INT, (GLvoid*)offsetOfBackWall);
	glDisableVertexAttribArray(PosLoc);
	glDisableVertexAttribArray(TexLoc);
	glDisableVertexAttribArray(bStrechLoc);
}

void CCameraMeshInstance::Update( const float time, const CViewerCamera& Cam )
{
	m_WVP = Cam.m_ProjectionTransform*Cam.m_ViewTransform*m_World;
}

SBoundingBox CCameraMeshInstance::GetWorldBoundingBox() const
{
	SBoundingBox MeshBB = ((CCameraMesh*)m_pMesh)->GetBoundingBox();
	SBoundingBox Ret;
	Ret.Min	= m_World * MeshBB.Min;
	Ret.Max	= m_World * MeshBB.Max;
	return Ret;
}

void CCameraMeshInstance::SetScale( float z )
{
	m_Scale.InitScaleTransform(z, z, z);
}

void CCameraMeshInstance::DrawPicking() const
{
	//drawing the camera frustrum
	glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
	glEnable(GL_DEPTH_TEST);
	glDisable(GL_CULL_FACE);
	CCameraMesh* pMesh = (CCameraMesh*)m_pMesh;
	VISIO_ASSERT(pMesh);
	glBindBuffer(GL_ARRAY_BUFFER, pMesh->m_VBO[0]);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pMesh->m_IBO[0]);

	GLuint progObject = pMesh->m_GLPicking.pProgram->GetProgramObject();
	glUseProgram(progObject);
	GLuint WVPLoc = pMesh->m_GLPicking.vsVars["WVPMat"];
	GLuint PosLoc = pMesh->m_GLPicking.vsVars["Position"];
	GLuint bStrechLoc = pMesh->m_GLPicking.vsVars["bStreched"];
	GLuint StrechFactor = pMesh->m_GLPicking.vsVars["strechFactor"];
	GLuint indexLoc = pMesh->m_GLPicking.psVars["gIndex"];
	

	glUniformMatrix4fv(WVPLoc, 1, GL_TRUE, &m_WVP.m[0][0]);
	glUniformMatrix4fv(StrechFactor, 1, GL_TRUE, &m_Scale.m[0][0]);
	glUniform1i(indexLoc, m_Index);
	glEnableVertexAttribArray(PosLoc);
	glEnableVertexAttribArray(bStrechLoc);
	glVertexAttribPointer(PosLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
	glVertexAttribPointer(bStrechLoc, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)24);

	glDrawElements(GL_TRIANGLES, pMesh->m_NumIndices, GL_UNSIGNED_INT, 0);
}

CQuadMeshInstance::CQuadMeshInstance( CQuadMesh* pMesh )
{
	m_pMesh = pMesh;
}

void CQuadMeshInstance::Draw() const
{
	Matrix4f I;
	I.InitIdentity();

	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	CQuadMesh* pMesh = (CQuadMesh*)m_pMesh;
	pMesh->m_Texture.Bind(GL_TEXTURE0);
	VISIO_ASSERT(pMesh);
	glBindBuffer(GL_ARRAY_BUFFER, pMesh->m_VBO[0]);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, pMesh->m_IBO);
	
	GLuint progObject = pMesh->m_GLMethod.pProgram->GetProgramObject();
	glUseProgram(progObject);
	GLuint WVPLoc = pMesh->m_GLMethod.vsVars["WVPMat"];
	GLuint PosLoc = pMesh->m_GLMethod.vsVars["Position"];
	GLuint TexLoc = pMesh->m_GLMethod.vsVars["iTexCoord"];
	GLuint SamLoc = pMesh->m_GLMethod.psVars["gSampler"];
	glUniformMatrix4fv(WVPLoc, 1, GL_TRUE, &I.m[0][0]);
	glUniform1i(SamLoc, 0);
	glEnableVertexAttribArray(PosLoc);
	glEnableVertexAttribArray(TexLoc);
	glVertexAttribPointer(PosLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
	glVertexAttribPointer(TexLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)12);

	glDrawElements(GL_TRIANGLES, pMesh->m_NumIndices, GL_UNSIGNED_INT, 0);
}

void CQuadMeshInstance::Update( const float time, const CViewerCamera& Cam )
{
	
}

CPointCloudMeshInstance::CPointCloudMeshInstance( CPointCloudMesh* pMesh )
{
	m_pMesh = pMesh;

	Matrix4f I;
	I.InitIdentity();

	m_WVP = I;
}

void CPointCloudMeshInstance::Draw() const
{
	CPointCloudMesh* pMesh = (CPointCloudMesh*)m_pMesh;
	VISIO_ASSERT(pMesh);
	glBindBuffer(GL_ARRAY_BUFFER, pMesh->m_VBO[0]);

	GLuint progObject = pMesh->m_GLMethod.pProgram->GetProgramObject();
	glUseProgram(progObject);
	GLuint WVPLoc = pMesh->m_GLMethod.vsVars["WVPMat"];
	GLuint PosLoc = pMesh->m_GLMethod.vsVars["Position"];
	GLuint ColLoc = pMesh->m_GLMethod.vsVars["color"];
	
	glUniformMatrix4fv(WVPLoc, 1, GL_TRUE, &m_WVP.m[0][0]);
	glEnableVertexAttribArray(PosLoc);
	glEnableVertexAttribArray(ColLoc);
	glVertexAttribPointer(PosLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), 0);
	glVertexAttribPointer(ColLoc, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)12);
	glPointSize(3.0);
	glDrawArrays(GL_POINTS, 0, pMesh->m_NumVertices);

	glDisableVertexAttribArray(PosLoc);
}

void CPointCloudMeshInstance::Update( const float time, const CViewerCamera& Cam )
{
	m_WVP = Cam.m_ProjectionTransform*Cam.m_ViewTransform;
}

SBoundingBox CPointCloudMeshInstance::GetBoundingBox() const
{
	return ((CPointCloudMesh*)m_pMesh)->GetBoundingBox();
}
